package com.pnemec.swing;

import javax.swing.*;

import com.pnemec.util.DataProvider;

import test.AnimatedProgressBarTest;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
import java.util.LinkedList;

public class AnimatedProgressBar extends JPanel implements SimpleEventListener {
	/*
	 * how long sleep before animate next part
	 */
	private static final long SLEEP = 70;
	/* how many parts to draw, should divide 360 */
	private static final int PARTS = 18;
	private static final int PARTS_AT_ONE = 6; // it is nice if
	// PARTS/PARTS_AT_ONE = 4
	public static final int STATE_NORMAL = 1;
	public static final int STATE_ANIMATE = 2;
	public static final int STATE_FINISHED = 3;

	private static final String OK_TEXT = "OK";

	private static final Color baseColor = Color.black;
	private double angleAdd = 0;
	private int size;
	private Dimension dimension = null;
	private double angle = 0;
	private Shape shape = null;
	private Thread animator = null;
	private int state = -1;
	private Image okImage = null;

	public AnimatedProgressBar(int size) {
		this(size, false);
	}

	public AnimatedProgressBar(int size, boolean startAnimation) {
		super();
		this.size = size;
		init();
		if (startAnimation)
			startAnimation();
	}

	private void init() {
		this.dimension = new Dimension(size, size);
		angle = 2d * Math.PI * Math.random();
		angleAdd = Math.toRadians((360d / AnimatedProgressBar.PARTS));

		/* construct shape */
		float sizef = (float) size;
		float width = sizef / 20;
		width = width > 0 ? width : 1f;

		this.shape = new RoundRectangle2D.Float(sizef / 6, 0, sizef / 3, width,
				width, width);
		this.state = STATE_NORMAL;
	}

	private void startAnimation() {
		if (animator == null) {
			animator = new Thread(new Animator(this));
			animator.start();
		}
	}

	/**
	 * set image which will be displayed when state is set to OK_STATE
	 * 
	 * @param i -
	 *            square image, it will be resized to size
	 */
	public void setOKImage(Image i) {
		this.okImage = i;
	}

	public void setOKImage(String fileName) {
		Image image = DataProvider.getImage(fileName, size);
		if (image != null)
			this.setOKImage(image);

	}

	/**
	 * set one of predefined state see AnimatedProgressBar static fields *_STATE
	 * 
	 * @param state
	 */
	public void setState(int state) {
		if (state != STATE_ANIMATE && state != STATE_NORMAL
				&& state != STATE_FINISHED) {
			System.out.println("Invalid state");
			return;
		}
		this.state = state;
		if (state == STATE_ANIMATE && animator == null) {
			startAnimation();
			//System.out.println("starting animation");
		}
		// else
		repaint();
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		// System.out.println("APB.paintComponent() called");
		Color origColor = g.getColor();

		Graphics2D g2d = null;
		if (g instanceof Graphics2D) {
			g2d = (Graphics2D) g;
			g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
			if (state == STATE_ANIMATE) {
				// if we need preserve transform (children)
				// AffineTransform at = g2d.getTransform();
				g2d.setColor(Color.gray);
				angle += angleAdd;
				g2d.translate(size / 2, size / 2);
				g2d.rotate(angle);

				float alphaAdd = 1f / (float) PARTS_AT_ONE;
				float alpha = 0;
				for (int i = 0; i < PARTS_AT_ONE; i++) {
					alpha += alphaAdd;
					g2d.setColor(new Color(baseColor.getRed(), baseColor
							.getGreen(), baseColor.getBlue(), alpha));
					g2d.rotate(angleAdd);
					g2d.fill(shape);

				}
			}
			if (state == STATE_FINISHED) {
				g2d.setColor(baseColor);
				if (okImage == null) {
					Font f = (g2d.getFont()).deriveFont(0.7f * (float) size);
					g2d.setFont(f);
					g2d.drawString(OK_TEXT, 0, (int) (0.75f * size));
				} else {
					// System.out.println("printing image:
					// "+okImage.getSource());
					g2d.drawImage(okImage, 0, 0, size, size, null);
				}
			}
			// g2d.setTransform(at);
		} else {
			g.setColor(Color.RED);
			g.fillRect(1, 1, size, size);

		}
		g.setColor(origColor);
	}

	/**
	 * set dimension for this component
	 * 
	 * @return - Dimension or null
	 */
	public Dimension getPreferredSize() {
		return dimension;
	}

	/**
	 * handle event changes
	 */
	public void notifyEvent(SimpleEventAdapter s) {
		this.setState(s.getEventValue());
		//System.out.println("APB notify event: "+state);

	}

	public Dimension getPreffereDimension() {
		return dimension;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// small test suite

		new AnimatedProgressBarTest();
	}

	/**
	 * I do not want main AnimatedProgressBar implement runnable itself, so
	 * private inner class.
	 * 
	 * @author Pavel Nemec <pnemec@suse.cz>
	 * 
	 */
	private class Animator implements Runnable {
		private AnimatedProgressBar bar = null;

		public Animator(AnimatedProgressBar bar) {
			this.bar = bar;
		}

		public void run() {
			while (bar.isVisible()) {
				/* animate only when we we should */
				if (bar.state == STATE_ANIMATE)
					bar.repaint();
				try {
					Thread.sleep(bar.SLEEP);
				} catch (InterruptedException e) {
					// just continue
				}

			}
			//System.out.println("exiting Animator");
		}

	}

}
